#pragma once

#include "List.h"

//default memory allocation chunk size
#define MEM_ALLOC_SIZE 2048

//Templated Memory Data
//* holds data and a active flag
template <typename TYPE>
struct MemData
{
	bool m_active;
	TYPE m_data;
};

//Templated Memory Node
//* hold an array of MemData
template <typename TYPE>
struct MemNode
{
	List<MemData<TYPE>*> m_used;
	List<MemData<TYPE>*> m_free;
	MemData<TYPE>* m_pool;
};

//Templated Memory Pool
//* a vector list of nodes containing blocks of memory
template <typename TYPE>
class MemPool
{
private:
	//attributes
	List<MemNode<TYPE>*> m_list;
	int m_size;
	int m_used;
	int m_cursor;
public:
	//ctor & dtor
	MemPool(int a_size = MEM_ALLOC_SIZE)
	{
		//set attributes
		m_size = a_size;
		m_used = 0;
		m_cursor = 0;

		//allocate the first block of memory ready for use
		allocBlock();
	}

	~MemPool(){release();}

	void release()
	{
		//release all allocated memory
		for(int i = 0; i < m_list.size(); i++)
		{
			MemNode<TYPE>* blockNode = m_list[i];
			blockNode->m_used.release();
			blockNode->m_free.release();
			delete[] blockNode->m_pool;
			delete blockNode;
		}
		m_list.release();

		m_size = 0;
		m_used = 0;
		m_cursor = 0;
	}

	void allocBlock()
	{
		//create a block node
		MemNode<TYPE>* blockNode = new MemNode<TYPE>;

		//create a data block
		MemData<TYPE>* blockData = new MemData<TYPE>[m_size];

		//loop and activate all memory chunks to false
		//then added it into the free memory list
		for(int i = 0; i < m_size; i++)
		{
			blockData[i].m_active = false;
			blockNode->m_free.insertBack(&blockData[i]);
		}

		//give data block to block node
		blockNode->m_pool = blockData;

		//add the block node to block list
		m_list.insertBack(blockNode);
	}

	TYPE* newData(int* a_block = 0, int* a_index = 0)
	{
		MemNode<TYPE>* blockNode = m_list[m_cursor];

		MemData<TYPE>* blockData = blockNode->m_free.removeFront();
		blockData->m_active = true;

		if(a_block != 0)
			*a_block = m_cursor;

		if(a_index != 0)
			*a_index = blockNode->m_used.size();

		blockNode->m_used.insertBack(blockData);

		++m_used;

		return &blockData->m_data;
	}

	void deleteData(TYPE** a_data, int* a_block = 0, int* a_index = 0)
	{
		MemNode<TYPE>* blockNode = 0;
		MemData<TYPE>* blockData = 0;

		if(a_block != 0 && a_index != 0)
		{
			blockNode = m_list[*a_block];
			blockData = blockNode->m_used[*a_index];

			if(&blockData->m_data != *a_data)
				return;

			blockData->m_active = false;
			blockNode->m_free.insertBack(
				blockNode->m_used.removeData(blockData));
			*a_data = 0;
			--m_used;
			return;
		}

		for(int i = 0; i < m_list.size(); i++)
		{
			blockNode = m_list[i];
			for(int j = 0; j < blockNode->m_used.size(); j++)
			{
				blockData = blockNode->m_used[j];
				if(&blockData->m_data == *a_data)
				{
					blockData->m_active = false;
					blockNode->m_free.insertBack(
						blockNode->m_used.removeData(blockData));
					*a_data = 0;
					--m_used;
					return;
				}
			}
		}
	}

	void size()
	{
		return m_used;
	}
};